package cn.wenzy.aurora.framework.support.i18n.core;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.wenzy.aurora.framework.constant.IConstant;
import cn.wenzy.aurora.framework.support.i18n.properties.I18nProperties;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.LocaleUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.util.ResourceUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class I18nMessageLoader implements IConstant {
    private Map<Locale, Properties> localeMap;

    private I18nProperties i18nProperties;
    private String messageFilePrefix;
    private String messageFileSuffix;
    private String messageFilePattern;

    public I18nMessageLoader(@NotNull I18nProperties i18nProperties) {
        this.i18nProperties = i18nProperties;
        this.messageFilePrefix = i18nProperties.getMessageFilePrefix();
        this.messageFileSuffix = StrUtil.addPrefixIfNot(i18nProperties.getMessageFileSuffix(), StrUtil.DOT);
        this.messageFilePattern = StrUtil.concat(true, messageFilePrefix, STAR, messageFileSuffix);
        this.localeMap = new ConcurrentHashMap();
    }

    public void load() {
        if (ArrayUtil.isNotEmpty(i18nProperties.getPackages())){
            for (String packageName : i18nProperties.getPackages()){
                String path = StrUtil.replace(packageName, StrUtil.DOT, FileUtil.FILE_SEPARATOR);
                path = StrUtil.addSuffixIfNot(path, FileUtil.FILE_SEPARATOR);
                path = path.concat(messageFilePattern);
                Resource[] resources = getResources(path);
                loadResource(resources);
            }
        }
        if (ArrayUtil.isNotEmpty(i18nProperties.getFolders())){
            for (String folderPath : i18nProperties.getFolders()){
                File folder = getFolder(folderPath);
                if (ObjectUtil.isNotNull(folder)){
                    loadResource(folder);
                } else {
                    //当 folder 为 null 时的场景是 spring boot 的 jar 包启动时。要特殊处理
                    String path = StrUtil.addSuffixIfNot(folderPath, FileUtil.FILE_SEPARATOR);
                    path = path.concat(messageFilePattern);
                    Resource[] resources = getResources(path);
                    loadResource(resources);
                }
            }
        }
    }

    public Properties getProperties(@NotNull Locale locale) {
        return localeMap.get(locale);
    }

    private File getFolder(String folderPath) {
        String filePath = System.getProperty(SYSTEM_PROPERTY_BASEDIR);
        if (StrUtil.isNotBlank(filePath)){
            filePath = StrUtil.addSuffixIfNot(filePath, FileUtil.FILE_SEPARATOR);
            filePath = filePath.concat(folderPath);
        } else {
            filePath = ResourceUtils.CLASSPATH_URL_PREFIX.concat(folderPath);
        }
        try {
            return ResourceUtils.getFile(filePath);
        } catch (FileNotFoundException e) {
            log.warn("can not find file: {}", filePath);
        }
        return null;
    }

    private void loadResource(File file){
        if (!FileUtil.exist(file)){
            return;
        }
        if (FileUtil.isDirectory(file)){
            for (File f : file.listFiles()){
                loadResource(f);
            }
        } else {
            Locale locale = getLocaleFromFileName(file.getName());
            if (ObjectUtil.isNotNull(locale)){
                Properties properties = toProperties(file);
                addMessage(locale, properties);
            }
        }
    }

    private void loadResource(Resource[] resources){
        if (ArrayUtil.isEmpty(resources)){
            return;
        }
        for (Resource resource : resources){
            Locale locale = getLocaleFromFileName(resource.getFilename());
            if (ObjectUtil.isNotNull(locale)){
                Properties properties = toProperties(resource);
                addMessage(locale, properties);
            }
        }
    }

    private void addMessage(Locale locale, Properties properties) {
        if (ObjectUtil.isNull(locale) || MapUtil.isEmpty(properties)){
            return;
        }
        Properties prop = localeMap.get(locale);
        if (ObjectUtil.isNull(prop)){
            localeMap.put(locale, properties);
        } else {
            prop.putAll(properties);
        }
    }

    private Locale getLocaleFromFileName(String fileName) {
        if (StrUtil.startWith(fileName, messageFilePrefix) && StrUtil.endWith(fileName, messageFileSuffix)){
            String localename = StrUtil.removePrefix(fileName, messageFilePrefix);
            localename = StrUtil.removeSuffix(localename, messageFileSuffix);
            Locale locale = LocaleUtils.toLocale(localename);
            return LocaleUtils.isAvailableLocale(locale) ? locale : null;
        }
        return null;
    }

    private Properties toProperties(File file) {
        try {
            if (FileUtil.exist(file)){
                return null;
            }
           return toProperties(FileUtil.getInputStream(file));
        } catch (IOException e) {
            log.error("read file error", e);
        }
        return null;
    }

    private Properties toProperties(Resource resource) {
        try {
            if (ObjectUtil.isNull(resource) || !resource.exists()){
                return null;
            }
            return toProperties(resource.getInputStream());
        } catch (IOException e) {
            log.error("read file error", e);
        }
        return null;
    }

    private Properties toProperties(InputStream inputStream) throws IOException {
        Reader reader = null;
        try {
            reader = IoUtil.getReader(inputStream, StandardCharsets.UTF_8);
            Properties properties = new Properties();
            properties.load(reader);
            return properties;
        } catch (IOException e){
            throw e;
        } finally {
            IoUtil.close(inputStream);
            IoUtil.close(reader);
        }
    }

    private Resource[] getResources(String path) {
        try {
            return new PathMatchingResourcePatternResolver().getResources(path);
        } catch (Exception ex) {
            return new Resource[0];
        }
    }
}
